package com.google.gwt.gwtpages.client.message;

import java.util.ArrayList;
import java.util.List;

import com.google.gwt.event.shared.HandlerManager;
import com.google.gwt.gwtpages.client.Pages;
import com.google.gwt.gwtpages.client.event.MessageChangeEvent;
import com.google.gwt.user.client.ui.Focusable;
import com.google.gwt.user.client.ui.HasWidgets;

/**
 * Controller class for application notification messages. All messages must
 * implement the {@link Message} interface. In most cases, you can use
 * {@link SimpleMessage} as the standard implementation.
 * 
 * @author Joe Hudson
 */
public class Messages {

	private static Messages instance;

	protected HandlerManager eventBus;

	private List<Message> messages = new ArrayList<Message>();

	public static Messages get() {
		return instance;
	}

	public Messages(Pages pages) {
		if (null == pages)
			throw new IllegalArgumentException(
					"The GWT Pages instance must not be null");
		this.eventBus = pages.getEventBus();
		if (null == instance)
			instance = this;
	}

	public Messages() {
		if (null == instance)
			instance = this;
	}

	/**
	 * Clear all application messages and fire the {@link MessageChangeEvent}
	 * event
	 */
	public void clear() {
		messages.clear();
		fireChangeEvent();
	}

	/**
	 * Erase any existing messages and set the new messages. Then fire the
	 * {@link MessageChangeEvent} event
	 */
	public void setMessage(Message... message) {
		messages.clear();
		add(message);
	}

	/**
	 * Erase any existing messages and set the new messages. Then fire the
	 * {@link MessageChangeEvent} event.
	 * @param parentContainer used to bypass event-based messaging - useful
	 * to have a separate messages panel for a dialog box
	 */
	public void setMessage(HasWidgets parentContainer, Message... message) {
		// FIXME check the parent container for a message handler
		messages.clear();
		add(message);
	}

	/**
	 * Erase any existing messages and set the new messages. Then fire the
	 * {@link MessageChangeEvent} event
	 * @param parentContainer used to bypass event-based messaging - useful
	 * to have a separate messages panel for a dialog box
	 */
	public void setMessages(HasWidgets parentContainer, List<Message> messages) {
		// FIXME check the parent container for a message handler
		this.messages.clear();
		this.messages.addAll(messages);
		fireChangeEvent();
	}

	/**
	 * Erase any existing messages and set the new messages. Then fire the
	 * {@link MessageChangeEvent} event
	 */
	public void setMessages(List<Message> messages) {
		this.messages.clear();
		this.messages.addAll(messages);
		fireChangeEvent();
	}

	/**
	 * Add the new messages but do not clear the existing messages. Then fire
	 * the {@link MessageChangeEvent} event
	 */
	public void add(Message... message) {
		for (Message _message : message) {
			messages.add(_message);
		}
		fireChangeEvent();
	}

	/**
	 * Add the new messages but do not clear the existing messages. Then fire
	 * the {@link MessageChangeEvent} event
	 * @param parentContainer used to bypass event-based messaging - useful
	 * to have a separate messages panel for a dialog box
	 */
	public void add(HasWidgets parentContainer, Message... message) {
		// FIXME check the parent container for a message handler
		for (Message _message : message) {
			messages.add(_message);
		}
		fireChangeEvent();
	}

	/**
	 * Auto-set a success message. Then fire the {@link MessageChangeEvent}
	 * event
	 * 
	 * @param message
	 *            the message text
	 */
	public void success(String message) {
		setMessage(Message.success(message));
	}

	/**
	 * Auto-set an error message. Then fire the {@link MessageChangeEvent} event
	 * 
	 * @param message
	 *            the message text
	 */
	public void error(String message) {
		setMessage(Message.error(message));
	}

	/**
	 * Auto-set an error message. Then fire the {@link MessageChangeEvent} event
	 * 
	 * @param message
	 *            the message text
	 * @param component the component that is in error
	 */
	public void error(String message, Focusable component) {
		setMessage(Message.error(message, component));
	}

	private void fireChangeEvent() {
		if (null == eventBus)
			eventBus = Pages.get().getEventBus();
		eventBus.fireEvent(new MessageChangeEvent(messages));
	}

	/**
	 * Interface defining a container for messages which can be attached to a
	 * request seesion for example
	 * 
	 * @author Joe Hudson
	 */
	public interface Data {
		public List<Message> getMessages();
	}
}